﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PConexion;
using System.IO;
using PManejadorArchivo;
using System.Threading;

namespace PProtocolo
{
    public abstract class Protocolo
    {
        public enum Tipo {RES,REQ};
        public Conexion conexion;
        
        public int EnviarTrama(Trama trama)
        {
            Thread.Sleep(100);
            int retorno = 0;
            StringBuilder tramaSB = new StringBuilder();
            if (trama.Header == Protocolo.Tipo.REQ)
            {
                //Es un REQ
                tramaSB.Append("REQ");
            }
            else
            {
                tramaSB.Append("RES");
            }

            tramaSB.Append(trama.Comando.ToString("00"));

            tramaSB.Append(trama.Largo.ToString("0000"));

            if (trama.Datos.Length > 0)
            {
                tramaSB.Append(trama.Datos);
            }

            String tramaString = tramaSB.ToString();

            char[] tramaChar = tramaString.ToCharArray();

            byte[] tramaByte = new byte[tramaSB.Length];

            int i = 0;

            for (; i < tramaChar.Length; i++)
            {
                tramaByte[i] = Convert.ToByte(tramaChar[i]);
            }

            //No es necesario completar con ceros
            //ya que por defecto el buffer vacio tiene
            //valor cero.

            retorno = conexion.EnviarDatos(tramaByte);
            return retorno;

        }
        public Trama RecibirTrama()
        {
            Trama retorno = new Trama();

            byte[] buffer = new byte[10008];

            int bytes_recibidos = 0;

            bytes_recibidos = this.conexion.RecibirDatos(ref buffer);

            

            //if (bytes_recibidos>0)
            if (String.Compare(Convert.ToChar(buffer[0]).ToString(),"R") == 0)
            {
                //Parseamos los datos recibidos

                //Header
                StringBuilder header = new StringBuilder();
                for (int i = 0; i < 3; i++)
                {
                    header.Append(Convert.ToChar(buffer[i]).ToString());
                }
                String headerString = header.ToString();

                //Comando
                StringBuilder comando = new StringBuilder();
                for (int i = 3; i < 5; i++)
                {
                    comando.Append(Convert.ToChar(buffer[i]).ToString());
                }
                int comandoInt = 0;

                int.TryParse(comando.ToString(), out comandoInt);

                //Largo
                StringBuilder largo = new StringBuilder();
                for (int i = 5; i < 9; i++)
                {
                    largo.Append(Convert.ToChar(buffer[i]).ToString());
                }
                int largoInt = 0;

                int.TryParse(largo.ToString(), out largoInt);

                //Datos
                StringBuilder datos = new StringBuilder();
                for (int i = 9; i < 9 + largoInt; i++)
                {
                    datos.Append(Convert.ToChar(buffer[i]).ToString());
                }

                String datosString = datos.ToString();

                if (String.Compare(headerString, "RES")==0)
                {
                    retorno.Header = Protocolo.Tipo.RES;
                }
                else
                {
                    retorno.Header = Protocolo.Tipo.REQ;
                }

                retorno.Comando = comandoInt;

                retorno.Largo = largoInt;

                retorno.Datos = datosString;

            }
            
            
            return retorno;

        }

        public int EnviarTrama(TramaBIN trama)
        {
            int retorno = 0;

            byte[] tramaByte = new byte[trama.Largo + 9];

            String header;
            if (trama.Header == Protocolo.Tipo.REQ)
            {
                //Es un REQ
                header = "REQ";
            }
            else
            {
                header = "RES";
            }
            char[] headerChar = header.ToCharArray();
            for (int i = 0; i < 3; i++)
            {
                tramaByte[i] = Convert.ToByte(headerChar[i]);
            }

            char[] comandoChar = trama.Comando.ToString("00").ToCharArray();
            int j = 0;
            for (int i = 3; i < 5; i++)
            {
                tramaByte[i] = Convert.ToByte(comandoChar[j]);
                j++;
            }

            char[] largoChar = trama.Largo.ToString("0000").ToCharArray();

            int l=0;
            for (int i = 5; i < 9; i++)
            {
                tramaByte[i] = Convert.ToByte(largoChar[l]);
                l++;
            }

            int k = 0;
            for (int i = 9; i < trama.Largo + 9; i++)
            {
                tramaByte[i] = trama.Buffer[k];
                k++;
            }

            //No es necesario completar con ceros
            //ya que por defecto el buffer vacio tiene
            //valor cero.

            retorno = conexion.EnviarDatos(tramaByte);

            return retorno;

        }

        public TramaBIN RecibirTramaBIN()
        {
            
            byte[] buffer = new byte[10008];

            int bytes_recibidos = 0;

            bytes_recibidos = this.conexion.RecibirDatos(ref buffer);

            TramaBIN retorno = new TramaBIN(bytes_recibidos - 9);//El buffer tendra 9 bytes menos debido al cabezal

            
            if (String.Compare(Convert.ToChar(buffer[0]).ToString(), "R") == 0)
            {
                //Parseamos los datos recibidos

                //Header
                StringBuilder header = new StringBuilder();
                for (int i = 0; i < 3; i++)
                {
                    header.Append(Convert.ToChar(buffer[i]).ToString());
                }
                String headerString = header.ToString();

                //Comando
                StringBuilder comando = new StringBuilder();
                for (int i = 3; i < 5; i++)
                {
                    comando.Append(Convert.ToChar(buffer[i]).ToString());
                }
                int comandoInt = 0;

                int.TryParse(comando.ToString(), out comandoInt);

                //Largo
                StringBuilder largo = new StringBuilder();
                for (int i = 5; i < 9; i++)
                {
                    largo.Append(Convert.ToChar(buffer[i]).ToString());
                }
                int largoInt = 0;

                int.TryParse(largo.ToString(), out largoInt);

                //Datos
                
                for (int i = 0; i <  retorno.Buffer.Length; i++)
                {
                    retorno.Buffer[i] = buffer[i + 9];
                }

                if (String.Compare(headerString, "RES") == 0)
                {
                    retorno.Header = Protocolo.Tipo.RES;
                }
                else
                {
                    retorno.Header = Protocolo.Tipo.REQ;
                }

                retorno.Comando = comandoInt;

                retorno.Largo = largoInt;

            }


            return retorno;
        }

        public void EstablecerConexion(Conexion conexion)
        {
            this.conexion = conexion;
        }

        // Valida que la respuesta al comando enviado sea correcto.
        public bool ValidarDato(Trama trama, Protocolo.Tipo header, int comando)
        {
            int score = 0;
            bool retorno = false;

            if (trama.Header == header)
            {
                score++;
            }
            if (trama.Comando == comando)
            {
                score++;
            }
            if (score == 2)
            {
                retorno = true;
            }

            return retorno;
        }

        public bool ValidarDato(TramaBIN trama, Protocolo.Tipo header, int comando)
        {
            int score = 0;
            bool retorno = false;

            if (trama.Header == header)
            {
                score++;
            }
            if (trama.Comando == comando)
            {
                score++;
            }
            if (score == 2)
            {
                retorno = true;
            }

            return retorno;
        }

        public void EnviarArchivoContenidoREQ(string ruta, long tamanio, Tipo headerEnvio, int comandoEnvio)
        {
            Console.WriteLine("Enviando archivo...");

            decimal vecesDecimal = tamanio / 9999;
            int veces = Convert.ToInt32(decimal.Truncate(vecesDecimal));
            decimal restoDecimal = tamanio - (veces * 9999);
            int resto = Convert.ToInt32(decimal.Truncate(restoDecimal));

            int bytes_leidos = 0;

            using (FileStream inputStream = new FileStream(ruta, FileMode.Open))
            {
                for (int i = 0; i < veces; i++)
                {
                    TramaBIN trama = new TramaBIN();
                    trama.Comando = comandoEnvio;
                    trama.Header = headerEnvio;
                    trama.Largo = 9999;

                    byte[] buffer = new byte[9999];
                    if ((bytes_leidos = inputStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        for (int j = 0; j < 9999; j++)
                        {
                            trama.Buffer[j] = buffer[j];
                        }

                        EnviarTrama(trama);
                    }
                    Console.Write(".");
                }


                //Si quedaron datos sin recorrer los recorro
                if (resto > 0)
                {
                    TramaBIN trama = new TramaBIN(resto);
                    trama.Comando = comandoEnvio;
                    trama.Header = headerEnvio;
                    trama.Largo = Convert.ToInt32(resto);
                    byte[] buffer = new byte[resto];
                    if ((bytes_leidos = inputStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        for (int i = 0; i < resto; i++)
                        {
                            trama.Buffer[i] = buffer[i];
                        }

                        EnviarTrama(trama);
                    }
                    Console.WriteLine(".");


                }

                inputStream.Close();
                Console.WriteLine("EXITO: Finalizo envio.");
            }

        }

        public void RecibirArchivoContenidoRES(long tamanio, string nombre, Tipo headerValidacion, int comandoValidacion)
        {
            int contador = 0;

            IManejadorArchivo ma = new ManejadorArchivo();

            Archivo archivoRecibido = ma.AbrirArchivoEscritura(nombre);

            decimal vecesDecimal = tamanio / 9999;
            int veces = Convert.ToInt32(decimal.Truncate(vecesDecimal));

            decimal restoDecimal = tamanio - (veces * 9999);
            int resto = Convert.ToInt32(decimal.Truncate(restoDecimal));

            for (int i = 0; i < veces; i++)
            {
                TramaBIN trama = RecibirTramaBIN();
                if (ValidarDato(trama, headerValidacion, comandoValidacion))
                {

                    archivoRecibido.Escribir(trama.Buffer);
                    contador++;

                }
            }

            //Si quedaron datos sin recorrer los recorro
            if (resto > 0)
            {
                TramaBIN trama = RecibirTramaBIN();
                archivoRecibido.Escribir(trama.Buffer);
                contador++;


            }

            archivoRecibido.Cerrar();
        }


    }
}
